﻿//Copyright (c) Microsoft Corporation.  All rights reserved.

using BookSleeve;
using Microsoft.Ted.Wacel.Base;
using System;
using System.Text;
using System.Threading;

namespace Microsoft.Ted.Wacel.Redis
{
    public class RedisLockProvider: Base.ILockProvider
    {
        private RedisConnection mConnection;
        private string mName;

        public RedisLockProvider(RedisConnection connection, string name)
        {
            if (connection == null)
                throw new ArgumentNullException("RedisConnection can't be null.");
            mConnection = connection;
            mConnection.Open();
            mName = name;
        }
        public void PutInSub(string key, long value)
        {
            mConnection.Strings.Set(0, mName + ":" + key, value);
        }
    
        public long Increment(string key, long increment, long defaultValue)
        {
            //TODO: defaultValue
            return mConnection.Wait<long>(mConnection.Strings.Increment(0, mName + ":" + key, increment));
        }

        public long ReadAndLock(string key, long defaultValue, TimeSpan timespan, out object lockHandle)
        {
            bool ret = false;
            lockHandle = Guid.NewGuid().ToString("N");
            while (!ret)
            {
                ret = mConnection.Wait(mConnection.Strings.TakeLock(0, mName + ":" + key + ":lock", (string)lockHandle, (int)timespan.TotalSeconds));
                if (ret)
                {
                    var data = mConnection.Wait(mConnection.Strings.Get(0, mName + ":" + key));
                    if (data == null)
                        return defaultValue;
                    else 
                        return (long)JsonSerializer.Deserialize(typeof(long), data);
                }
                else
                    Thread.Sleep(500);
            }
            return defaultValue;
        }


        public void PutAndUnlock(string key, long value, object lockHandle)
        {
            var data = mConnection.Wait(mConnection.Strings.Get(0, mName + ":" + key + ":lock"));
            if (data != null)
            {
                if (Encoding.ASCII.GetString(data) == (string)lockHandle)
                {
                    mConnection.Wait(mConnection.Strings.Set(0, mName + ":" + key, value.ToString()));
                    mConnection.Wait(mConnection.Keys.Remove(0, mName + ":" + key + ":lock"));
                }
            }
        }

        public void Unlock(string key, object lockHandle)
        {
            var data = mConnection.Wait(mConnection.Strings.Get(0, mName + ":" + key + ":lock"));
            if (data != null)
            {
                if (Encoding.ASCII.GetString(data) == (string)lockHandle)
                {
                    mConnection.Wait(mConnection.Keys.Remove(0, mName + ":" + key + ":lock"));
                }
            }
        }

        public void Clear()
        {
            mConnection.Server.FlushDb(0).Wait();
        }
    }
}
